package gov.dhs.cis.soa.xslt;

import gov.dhs.cis.soa.Utils;
import gov.dhs.cis.soa.data.Description;
import gov.dhs.cis.soa.data.Service;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;

import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class UpdateRSAFile {
	XPathFactory xpathFactory = XPathFactory.newInstance();
	String XMI_NS = "http://schema.omg.org/spec/XMI/2.1";
	String WBIM_NS = "http://www.ibm.com/wbim/bomSchema1.0";

	private Document getXMLDocument(String inXML) throws Exception {
		InputStream inXMLStream = this.getClass().getClassLoader().getResourceAsStream(inXML);
		DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
		domFactory.setNamespaceAware(true);
		DocumentBuilder builder = domFactory.newDocumentBuilder();
		Document doc = builder.parse(inXMLStream);
		return doc;
	}
	
	private NamespaceContext getNameSpaceCtx () {
		NamespaceContext ctx = new NamespaceContext() {
	        public String getNamespaceURI(String prefix) {
	        	//System.out.println("Name space requested : " + prefix);
	            String uri = null;
	            if (prefix.equals("xmi")) {
	                uri = XMI_NS;
	            } else if (prefix.equals("wbim")) {
		                uri = WBIM_NS;
	            }
	            return uri;
	        }
	       
	        // Dummy implementation - not used!
	        public Iterator getPrefixes(String val) { return null; }
	       
	        // Dummy implemenation - not used!
	        public String getPrefix(String uri) { return null; }
	    };
	    return ctx;
	}
	
	private XPathExpression getXPathExpression(String xpathStr) throws Exception {
		XPath xpath = xpathFactory.newXPath();
		xpath.setNamespaceContext(getNameSpaceCtx());
		// XPath Query for showing all nodes value
		XPathExpression expr = xpath.compile(xpathStr);
		return expr;
	}

	private NodeList evaluate(Object evalOn, XPathExpression expr) throws Exception {
		Object result = expr.evaluate(evalOn, XPathConstants.NODESET);
		NodeList nodes = (NodeList) result;
		return nodes;
	}

	private ArrayList<Service> extractWBMServices(Document doc) throws Exception {
		ArrayList<Service> servList = new ArrayList<Service>();

		NodeList nodes = evaluate(doc, getXPathExpression("//wbim:processModel/wbim:services/wbim:service"));
		XPathExpression servDescExpr = getXPathExpression("wbim:description/text()");
		
		String servName = "";
		String WIPT = "";
		String desc = "";
		for(int i = 0; i < nodes.getLength(); i++) {
			Node servNode = nodes.item(i);
			
			servName = servNode.getAttributes().getNamedItem("name").getNodeValue();
			WIPT = "";
			int idx = servName.indexOf("##");
			if(idx > 0) {
				WIPT = servName.substring(0, idx);
				servName = servName.substring(idx+2);
			}
			Service servO = new Service();
			servO.name = servName;
			servO.wipt = WIPT;
			NodeList descNodes = evaluate(servNode, servDescExpr);
			if(descNodes != null && descNodes.getLength() > 0) {
				desc = descNodes.item(0).getNodeValue();
				ServicesUtils.parseDescription(servO, desc, null);
			}
			//System.out.println(servName + " === " + desc);

			servList.add(servO);
		}
		return servList;
	}

	private void updateRSAServices(Document doc, ArrayList<Service> servList) throws Exception {

		NodeList nodes = evaluate(doc, getXPathExpression("//packagedElement[@name='007_Services']/packagedElement[@xmi:type='uml:Class']"));
		XPathExpression servDescExpr = getXPathExpression("ownedComment/body/text()");
		
		String servName = "";
		String desc = "";
		for(int i = 0; i < nodes.getLength(); i++) {
			Node servNode = nodes.item(i);
			
			servName = servNode.getAttributes().getNamedItem("name").getNodeValue();
			desc = "";
			
			NodeList descNodes = evaluate(servNode, servDescExpr);
			if(descNodes != null && descNodes.getLength() > 0) {
				//Description desc = new Description();
				desc = descNodes.item(0).getNodeValue();
				//descNodes.item(0).setNodeValue("Updates from WBM - RSingh " + ServicesUtils.lineSep + desc);
				descNodes.item(0).setNodeValue(desc);
			}
			//System.out.println(servName + " === " + desc);
			
			//servList.add(servO);
		}
		return;
	}
	
	private void compNprintRSAServDiffs(Document doc, ArrayList<Service> servList) throws Exception {

		NodeList nodes = evaluate(doc, getXPathExpression("//packagedElement[@name='007_Services']/packagedElement[@xmi:type='uml:Class']"));
		XPathExpression servDescExpr = getXPathExpression("ownedComment/body/text()");
		
		//String servName = "";
		String desc = "";
		
		Node attribNode;
		Service serv = null;
		for(int i = 0; i < nodes.getLength(); i++) {
			Node servNode = nodes.item(i);
			serv = new Service();
			serv.name = servNode.getAttributes().getNamedItem("name").getNodeValue();

			String sXMI_ID = servNode.getAttributes().getNamedItemNS(XMI_NS, "id").getNodeValue();
			
			String xp = "//packagedElement[@xmi:type='uml:Association']/*[contains(@xmi:id, '" + sXMI_ID + "')]";
			XPathExpression associationsExpr = getXPathExpression(xp);
			NodeList associationNodes = evaluate(servNode, associationsExpr);
			for(int j = 0; j < associationNodes.getLength(); j++) {
				attribNode = associationNodes.item(j);
				ServicesUtils.parseAssociation(serv, attribNode.getAttributes().getNamedItemNS(XMI_NS, "id").getNodeValue());
			}
			if(!serv.release.equalsIgnoreCase("A")) {
				//System.out.println("Service | " + serv.name + "| Not Release A");
				continue;
			}
			Service wmbService = Utils.findServiceByName(serv.name.trim(), servList);
			if(wmbService == null) {
				System.out.println("Service | " + serv.name + "| not found in RSA.");
				continue;
			}
			
			/*
			desc = "";
			
			NodeList descNodes = evaluate(servNode, servDescExpr);
			if(descNodes != null && descNodes.getLength() > 0) {
				//Description desc = new Description();
				desc = descNodes.item(0).getNodeValue();
				//descNodes.item(0).setNodeValue("Updates from WBM - RSingh " + ServicesUtils.lineSep + desc);
				descNodes.item(0).setNodeValue(desc);
			}
			//System.out.println(servName + " === " + desc);
			
			//servList.add(servO);
			*/
		}
		return;
	}

	public void read(String inRSAXMIFileName, String inWBMXMLFileName, String outXMIFileName) throws Exception {
		Document wbmDoc = getXMLDocument(inWBMXMLFileName);
		ArrayList<Service> wbmServList = extractWBMServices(wbmDoc);
		System.out.println("=================================================================");
		Document rsaDoc = getXMLDocument(inRSAXMIFileName);
		updateRSAServices(rsaDoc, wbmServList);
		transform2Txt(rsaDoc, outXMIFileName);
	}
	
	public void compareWBMnRSAModels(String inRSAXMIFileName, String inWBMXMLFileName) throws Exception {
		Document wbmDoc = getXMLDocument(inWBMXMLFileName);
		ArrayList<Service> wbmServList = extractWBMServices(wbmDoc);
		System.out.println("=================================================================");
		Document rsaDoc = getXMLDocument(inRSAXMIFileName);
		compNprintRSAServDiffs(rsaDoc, wbmServList);
	}

	private void transform2Txt(Document document, String fileName) throws Exception {
	    TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
        DOMSource source = new DOMSource(document);
        OutputStream fos = null;
        if(!Utils.isEmpty(fileName)) {
        	fos = new  FileOutputStream(fileName);
        } else {
        	fos = System.out;       	
        }
    	StreamResult result =  new StreamResult(fos);
        transformer.transform(source, result);
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		String inXMIFileName = "USCISIOEModel1.uml";
		String inWBMXMLFileName = "Common Model Elements.xml";	//"wmb_services.xml";
		String outXMIFileName = "USCISIOEModel1.uml";

		UpdateRSAFile rsaReader = new UpdateRSAFile();
		try {
			//rsaReader.read(inXMIFileName, inWBMXMLFileName, outXMIFileName);
			rsaReader.compareWBMnRSAModels(inXMIFileName, inWBMXMLFileName);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

}
